package com.significantfiles.properties.util.config.converter;

import java.lang.reflect.Constructor;

import org.apache.log4j.Logger;

public class GenericConverterFactory {

	private final Log LOG = new Log();

	public Converter<?> createConverter(final Class<?> paramType) {
		LOG.tryToIdentifyGenericConstructor(paramType);

		// TODO: use different types (and not only String.class)
		final Constructor<?> constructor = identifyConstructor(paramType,
				String.class);

		if (constructor == null) {
			return null;
		}

		//TODO: cache
		return new GenericStringBasedConstructorConverter(paramType, constructor);
	}

	
	
	private Constructor<?> identifyConstructor(final Class<?> paramType,
			final Class<?> constructorParamType) {
		try {
			return paramType.getConstructor(constructorParamType);
		} catch (final SecurityException e) {
			LOG
					.constructorSecurityException(paramType,
							constructorParamType, e);

			return null;
		} catch (final NoSuchMethodException e) {
			LOG.noSuchConstructor(paramType, constructorParamType, e);
			
			return null;
		}
	}

	private class Log {
		private Logger LOGGER = Logger.getLogger(GenericConverterFactory.class);

		public void constructorSecurityException(final Class<?> paramType,
				final Class<?> constructorParamType, final SecurityException e) {
			if (!LOGGER.isInfoEnabled())
				return;

			final String logMsg = "Couldn't use constructor [= " + paramType+ "] of passed type [= "
					+ paramType.getName()
					+ "] because constructor isn't PUBLIC [check Exception]. If you like to use this constructor you shoud make it public or write a converter for this passed type.";

			LOGGER.info(logMsg, e);
		}

		public void noSuchConstructor(final Class<?> paramType,
				final Class<?> constructorParamType,
				final NoSuchMethodException e) {
			if (!LOGGER.isInfoEnabled())
				return;

			final String logMsg = "Couldn't identify constructor [= "
					+ paramType.getSimpleName() + "( "
					+ constructorParamType.getName() + " ) ] in passed type [="
					+ paramType.getName() + "]. Use another primitive type to identify a constructor.";
			
			LOGGER.info( logMsg );
		}

		public void tryToIdentifyGenericConstructor(final Class<?> paramType) {
			if (!LOGGER.isInfoEnabled())
				return;

			LOGGER
					.info("Try to identify generic-constructor for passed type [= "
							+ paramType + "]");
		}
	}
	
}